package org.wisdom.ac.application.service;

import java.security.Key;
import java.util.*;
import java.util.stream.IntStream;

import javax.crypto.spec.SecretKeySpec;

import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.transaction.annotation.Transactional;
import org.wisdom.ac.domain.resource.entity.AcCaptcha;
import org.wisdom.ac.domain.resource.repository.CaptchaRepository;
import org.wisdom.ac.domain.user.entity.AcUser;
import org.wisdom.ac.domain.user.entity.AcUserRole;
import org.wisdom.ac.domain.user.repository.UserRepository;
import org.wisdom.ac.dto.AuthUserDto;
import org.wisdom.ac.dto.LoginInfo;
import org.wisdom.ac.dto.UserDto;
import org.wisdom.annotation.WisdomService;
import org.wisdom.context.WisdomContextHolder;
import org.wisdom.exception.BusinessException;
import org.wisdom.security.config.JwtSecurityProperties;
import org.wisdom.security.dto.ValidatedToken;
import org.wisdom.security.exception.TokenNotValidException;
import org.wisdom.security.utils.TokenValidator;

import com.google.code.kaptcha.impl.DefaultKaptcha;

import cn.hutool.core.bean.BeanUtil;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * 权限服务
 *
 * @author 张智研
 * @date 2022/05/30 14:23
 **/
@WisdomService
@Slf4j
@RequiredArgsConstructor
public class AuthService {

    private final UserRepository userRepository;

    private final CaptchaRepository captchaRepository;

    private final JwtSecurityProperties jwtSecurityProperties;

    private final PasswordEncoder passwordEncoder;

    private final TokenValidator tokenValidator;

    private final DefaultKaptcha defaultKaptcha;

    /**
     * 获取token
     * 
     * @author 张智研
     * @date 2022/09/04 14:54
     * @param username
     *            用户名
     * @param password
     *            密码
     * @return org.wisdom.ac.dto.LoginInfo
     */
    public LoginInfo getToken(String username, String password) {
        // 验证用户状态
        AcUser user = this.userRepository.findByUserNameAndEnabled(username, true);
        if (user == null) {
            throw new BusinessException("用户名或密码不正确!");
        }
        // 验证用户密码
        boolean b = this.passwordEncoder.matches(password, user.getPassword());
        if (!b) {
            throw new BusinessException("用户名或密码不正确!");
        }
        // 生成token
        String token = this.generateToken(username, user.getId());
        UserDto userDto = BeanUtil.copyProperties(user, UserDto.class, "password");
        LoginInfo loginInfo = new LoginInfo();
        loginInfo.setToken(token);
        loginInfo.setUser(userDto);
        return loginInfo;
    }

    /**
     * 获取当前登陆用户信息
     * 
     * @author 张智研
     * @date 2022/09/04 14:55
     * @return org.wisdom.vo.ResponseResult<org.wisdom.ac.dto.UserDto>
     */
    public AcUser getInfo() {
        String userId = WisdomContextHolder.getContext().getUserId();
        Optional<AcUser> userOptional = this.userRepository.findById(userId);
        if (userOptional.isPresent()) {
            return userOptional.get();
        } else {
            throw new BusinessException(1, "当前请求无登陆用户信息！");
        }
    }

    /**
     * 更新token
     *
     * @author 张智研
     * @date 2022/05/30 15:05
     * @param token
     *            token
     * @return java.lang.String
     */
    public String updateToken(String token) {
        // 解析token
        ValidatedToken validatedToken;
        try {
            validatedToken = this.tokenValidator.validate(token);
        } catch (TokenNotValidException e) {
            log.info("token无效，更新失败！");
            throw new BusinessException("token无效！");
        }
        // 验证用户状态
        AcUser user = this.userRepository.findByUserNameAndEnabled(validatedToken.getSubject(), true);
        if (user == null) {
            throw new BusinessException("用户不存在或者已被停用!");
        }
        // 生成新的token
        return this.generateToken(validatedToken.getSubject(), user.getId());
    }

    /**
     * 生成token
     *
     * @author 张智研
     * @date 2022/05/30 14:57
     * @param username
     *            用户名
     * @return java.lang.String
     */
    private String generateToken(String username, String userId) {
        Key key = new SecretKeySpec(this.jwtSecurityProperties.getTokenKey().getBytes(),
            SignatureAlgorithm.HS256.getJcaName());
        Map<String, Object> map = new HashMap<>();
        map.put("username", username);
        map.put("userId", userId);
        return Jwts.builder().setClaims(map).setIssuedAt(new Date())
            .setExpiration(new Date(System.currentTimeMillis() + this.jwtSecurityProperties.getTokenExpire()))
            .signWith(SignatureAlgorithm.HS256, key).compact();
    }

    public AcCaptcha generateCaptcha() {
        // 获取验证码文本内容
        String text = defaultKaptcha.createText();
        log.info("验证码文本内容：" + text);
        AcCaptcha captcha = new AcCaptcha();
        captcha.setText(text);
        return this.captchaRepository.save(captcha);
    }

    /**
     * 授权用户
     * 
     * @author 张智研
     * @date 2023/05/17 14:06
     * @param authUserDto
     *            授权用户信息
     */
    @Transactional
    public void authUser(AuthUserDto authUserDto) {
        List<AcUser> userList = this.userRepository.findAllById(Arrays.asList(authUserDto.getUserIds().split(",")));
        for (AcUser user : userList) {
            AcUserRole userRole = new AcUserRole();
            userRole.setUserId(user.getId());
            userRole.setRoleId(authUserDto.getRoleId());
            user.getUserRoles().add(userRole);
        }
        this.userRepository.saveAll(userList);
    }

    /**
     * 取消授权用户
     *
     * @author 张智研
     * @date 2023/05/17 14:06
     * @param authUserDto
     *            授权用户信息
     */
    @Transactional
    public void authUserCancel(AuthUserDto authUserDto) {
        List<AcUser> userList = this.userRepository.findAllById(Arrays.asList(authUserDto.getUserIds().split(",")));
        for (AcUser user : userList) {
            IntStream.range(0, user.getUserRoles().size())
                .filter(i -> user.getUserRoles().get(i).getRoleId().equals(authUserDto.getRoleId())).boxed().findFirst()
                .map(i -> user.getUserRoles().remove((int)i));
        }
        this.userRepository.saveAll(userList);
    }
}
